//=========================================================================
// Copyright (C) 2021 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

#include <new>
#include "dpram_utils.h"
#include "cots_config.h"
#include "pmalloc.h"
#include "pminternal.h"
#include "comotypes.h"

using namespace como;

int dpramInit()
{
    int i;

    // initial MainPartition
    unsigned char *addr = reinterpret_cast<unsigned char *>(MainPartitionStart);
    for (i = 0;  i < MainPartitionSize / SpinLock_Size;  i++) {
        /*como::Spinlock* _obj =*/new (addr) como::Spinlock();
        addr += SpinLock_Size;
    }

    // initial ManagerPartition
    addr = reinterpret_cast<unsigned char *>(ManagerPartitionStart);
    for (i = 0;  i < ManagerPartitionSize / SpinLock_Size;  i++) {
        /*como::Spinlock* _obj =*/new (addr) como::Spinlock();
        addr += SpinLock_Size;
    }
    return 0;
}

como::Spinlock *dpramGetSpinlock(int idSpinlock)
{
    if ((idSpinlock < 0) || (idSpinlock >= MainPartitionSize / SpinLock_Size)) {
        return nullptr;
    }

    return static_cast<como::Spinlock *>(&(
                            (reinterpret_cast<como::Spinlock *>(MainPartitionStart))[idSpinlock]));
}

int dpramSetkey(void *md, int keyidx, void *buffer, int valueCapacity)
{
    if ((keyidx < 0) || (keyidx >= PMALLOC_KEYS)) {
        return -1;
    }

    // 1. 先去做实际内存写工作，再去做锁处理工作
    void *new_data = pmalloc(md, valueCapacity);
    if (nullptr != new_data) {
        memcpy(new_data, buffer, valueCapacity);
    }
    else {
        return -1;
    }

    /*各分区逻辑有效key范围[0-255],但需根据机器序号转为物理key index*/
    mdesc *tmp = static_cast<struct mdesc *>(md);
    int keyLockidx = ((0 == tmp->partitionNo) ? keyidx : (keyidx + PMALLOC_KEYS));

    // 2. 获取资源锁
    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx % PMALLOC_2_KEYS].Lock();

    /*获取当前消费状态*/
    bool stateConsumed =
        (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].StateConsumed();

    void *old_data = pmalloc_getkey(md, keyidx);

    if (nullptr == old_data) {
        /*初始状态，当前key对应的value指针为nullptr,直接setkey*/
        pmalloc_setkey(md, keyidx, new_data);
    }
    else if (stateConsumed) {
        // 已被消费状态，当前key对应的value内存区域被消费完成,释放旧内存后setkey
        pmfree(md, old_data);
        pmalloc_setkey(md, keyidx, new_data);
    }
    else {
        // 未被消费状态，当前key对应的value区域内存还未被消费，需要把预先申请内存new_data释放
        pmfree(md, new_data);

        // 该分支直接返回错误，需要在分支内解锁
        (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                       PMALLOC_2_KEYS].Unlock();
        return -1;
    }

    // 3.有序处理资源释放工作

    // 已被消费&初始状态处理后，新数据尚未被消费，需要把消费状态重置
    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].ClearConsumed();
    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].Unlock();
    return 0;
}

int dpramFreeKey(void *md, int keyidx)
{
    if (keyidx < 0 || keyidx >= PMALLOC_KEYS) {
        return -1;
    }

    /*各分区逻辑有效key范围[0-255],但需根据机器序号转为物理key index*/
    mdesc *tmp = static_cast<struct mdesc *>(md);
    int keyLockidx = ((0 == tmp->partitionNo) ? keyidx : (keyidx + PMALLOC_KEYS));

    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                         PMALLOC_2_KEYS].Lock();
    /*获取当前消费状态*/
    bool stateConsumed =
        (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].StateConsumed();

    void *dpram_data = pmalloc_getkey(md, keyidx);
    if ((nullptr != dpram_data) && stateConsumed) {
        /*当keyidx对应的内存区域存在且被消费时,可以被主动Freekey*/
        pmfree(md, dpram_data);
        pmalloc_setkey(md, keyidx, nullptr);  //回到初始状态nullptr
    }
    else {
        (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                       PMALLOC_2_KEYS].Unlock();
        return -1;
    }

    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].ClearConsumed();
    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                PMALLOC_2_KEYS].Unlock();

    return 0;
}

void *dpramGetValue(void *md, int keyidx, void *buffer, int valueCapacity)
{
    if ((keyidx < 0) || (keyidx >= PMALLOC_KEYS)) {
        return nullptr;
    }

    /*各分区逻辑有效key范围[0-255],但需根据机器序号转为物理key index*/
    mdesc *tmp = static_cast<struct mdesc *>(md);
    int keyLockidx = ((0 == tmp->partitionNo) ? keyidx : (keyidx + PMALLOC_KEYS));

    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx % PMALLOC_2_KEYS].Lock();

    void *dpram_data = pmalloc_getkey(md, keyidx);
    if (nullptr != dpram_data) {
        memcpy(buffer, dpram_data, valueCapacity);
    }
    else {
        (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                       PMALLOC_2_KEYS].Unlock();
        return nullptr;
    }

    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                  PMALLOC_2_KEYS].SetConsumed();
    (reinterpret_cast<como::Spinlock *>(ManagerPartitionStart))[keyLockidx %
                                                  PMALLOC_2_KEYS].Unlock();

    return buffer;
}

void *dpramMountPartition(int idPartition)
{
    if ((idPartition < 0) || (idPartition >= PartitionNumber)) {
        return nullptr;
    }

    // todo : 需要对pmalloc_attach进行改造 &
    // 验证实际机器上的起始地址情况，暂时不可用
    void *md = pmalloc_attach(-1,
                        reinterpret_cast<void *>(PartitionStart[idPartition]),
                        PartitionSizes[idPartition],
                        true);

    //记录idPartition
    mdesc *tmp = static_cast<struct mdesc *>(md);
    tmp->partitionNo = idPartition;
    return md;
}
